package com.pudding.listener.read;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.pudding.entity.vo.ExcelVO;
import com.pudding.mapper.ExcelMapper;
import com.pudding.task.DeadMainThread;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.*;

/**
 * @Author Pudding
 * @Date 2025/3/27 11:34
 */
@Slf4j
@NoArgsConstructor
public class MutliReadListener extends AnalysisEventListener<ExcelVO> {

    private ExcelMapper excelMapper;

    // 通过构造器传递过来excelMapper对象
    public MutliReadListener(ExcelMapper excelMapper) {
        this.excelMapper = excelMapper;
    }

    /**
     * 使用线程安全集合
     * 1. 使用ArrayList创建一个可变长度的列表。
     * 2. 调用Collections.synchronizedList方法，将普通列表包装为线程安全的列表。
     */
    private List<ExcelVO> dataList = Collections.synchronizedList(new ArrayList<>());

    /**
     * 创建线程池必要参数
     */
    private static final int CORE_POOL_SIZE = 5;//核心线程数
    private static final int MAX_POOL_SIZE = 10;//最大线程数
    private static final int QUEUE_CAPACITY = 100;//队列大小
    private static final Long KEEP_ALIVE_TIME = 1L;//存活时间

    /**
     * 该代码的功能是将传入的ExcelVO对象添加到线程安全的列表dataList
     * 每读取一条数据，就会调用invoke方法
     *
     * @param data
     * @param context
     */
    @Override
    public void invoke(ExcelVO data, AnalysisContext context) {
        if (dataList != null) {
            dataList.add(data);
        }
    }


    /**
     * 读取完成之后，会调用doAfterAllAnalysed方法，做一些释放资源、数据清理的操纵
     *
     * @param context
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        //创建线程池
        ExecutorService executor = new ThreadPoolExecutor(CORE_POOL_SIZE,
                MAX_POOL_SIZE,
                KEEP_ALIVE_TIME,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(QUEUE_CAPACITY),
                new ThreadPoolExecutor.CallerRunsPolicy());

        //指定每个线程需要处理的导入数量，假设每个线程处理10000条
        int singleThreadDealCount = 100000;
        //根据假设每个线程需要处理的数量以及总数，计算需要提交到线程池的线程数量
        int threadsize = (dataList.size() / singleThreadDealCount) + 1;
        log.info("threadsize===>{}", threadsize);

        //计算需要导入的数据总数，用于拆分时线程需要处理数据时使用
        int rowsize = dataList.size() + 1;
        //测试开始时间
        long startTime = System.currentTimeMillis();
        //申明该线程需要处理数据的开始位置
        int startPosition = 0;
        //申明该线程需要处理数据的结束位置
        int endPosition = 0;
        //为了让每个线程执行完后回到当前线程，使用CountDownLatch,值为线程数，每次线程执行完就会执行countDown方法减1，为0后回到主线程
        CountDownLatch count = new CountDownLatch(threadsize);

        //计算每个线程要处理的数据
        for (int i = 0; i < threadsize; i++) {
            //如果是最后一个线程，为保证程序不发生空指针异常，特殊判断结束位置
            if ((i + 1) == threadsize) {
                //计算开始位置
                startPosition = (i * singleThreadDealCount);
                //当前线程为划分的最后一个线程，则取总数据的最后为此线程的结束位置
                endPosition = rowsize - 1;
            } else {
                //计算开始位置
                startPosition = (i * singleThreadDealCount);
                //计算结束位置
                endPosition = (i + 1) * singleThreadDealCount;
            }

            log.info("线程开启====》count:{},startPosition:{},endPosition:{}", count, startPosition, endPosition);
            DeadMainThread deadMainThread = new DeadMainThread(count, excelMapper, dataList, startPosition, endPosition);
            executor.execute(deadMainThread);
        }
        try {
            count.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //逻辑处理完，关闭线程池
        executor.shutdown();
        long endTime = System.currentTimeMillis();
        System.out.println("总耗时：" + (endTime - startTime));
    }
}
